﻿namespace Rialternative.CodeGenerators
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.ServiceModel.DomainServices.Server;
    using Microsoft.ServiceModel.DomainServices.Tools.TextTemplate.CSharpGenerators;

    /// <summary>
    /// C# code generator for domain operations authorizations.
    /// </summary>
    internal class AuthorizationCodeGenerator : CSharpDomainContextGenerator
    {
        /// <summary>
        /// Generates the EntitySet properties.
        /// </summary>
        protected override void GenerateEntitySets()
        {
            base.GenerateEntitySets();
            const string UserAccessor = "System.ServiceModel.DomainServices.Client.ApplicationServices.WebContextBase.Current.Authentication.User";
            List<string> domainServiceRequireRoles = this.DomainServiceDescription.Attributes.OfType<RequiresRoleAttribute>()
                                                                                             .SelectMany(rr => rr.Roles)
                                                                                             .ToList();
            foreach (var op in this.DomainServiceDescription.DomainOperationEntries)
            {
                List<string> requireRoleNames = op.Attributes.OfType<RequiresRoleAttribute>()
                                                             .SelectMany(rr => rr.Roles)
                                                             .Union(domainServiceRequireRoles)
                                                             .ToList();
                if (requireRoleNames.Any())
                {
                    this.GeneratePropertyHeader(op.Name);
                    this.Write(UserAccessor + ".IsInRole(\"" + requireRoleNames.First() + "\")");
                    requireRoleNames.Skip(1).ToList().ForEach(orr => this.Write("\n\t\t\t\t\t\t || " + UserAccessor + ".IsInRole(\"" + orr + "\")"));
                    this.GeneratePropertyFooter();
                }
                else if (op.Attributes.OfType<RequiresAuthenticationAttribute>().Any() ||
                         this.DomainServiceDescription.Attributes.OfType<RequiresAuthenticationAttribute>().Any())
                {
                    this.GeneratePropertyHeader(op.Name);
                    this.Write(UserAccessor + ".Identity.IsAuthenticated");
                    this.GeneratePropertyFooter();
                }
            }
        }

        /// <summary>
        /// Generates the property header.
        /// </summary>
        /// <param name="propertyName">Name of the property.</param>
        private void GeneratePropertyHeader(string propertyName)
        {
            this.WriteLine("public bool Can" + propertyName);
            this.WriteLine("{");
            this.WriteLine("\tget");
            this.WriteLine("\t{");
            this.Write("\t\treturn ");
        }

        /// <summary>
        /// Generates the property footer.
        /// </summary>
        private void GeneratePropertyFooter()
        {
            this.Write(";");
            this.WriteLine(String.Empty);
            this.WriteLine("\t}");
            this.WriteLine("}");
        }
    }
}
